<?php
// @formatter:off
/**
 * @file WCOptions.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-18 22:13:05 +0000 (Thu, 18 Jul 2013) $
 *
 * $Id: WCOptions.php 97 2013-07-18 22:13:05Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/branches/wcomix-1.0/includes/WCOptions.php $
 */
// @formatter:on

/**
 * @class WCOptions This class holds the logic to represent a command line option
 * with its triggers and the way to consume parameters.
 */
class WCOption {
	/**
	 * @var boolean This flag indicates if this option has been found in the
	 * command line.
	 */
	protected $_activated = false;
	/**
	 * @var string Option's name.
	 */
	protected $_name = false;
	/**
	 * @var string[] List of parameters that may activate this option.
	 */
	protected $_triggers = array();
	/**
	 * @var boolean This flag indicates this option was found and will take
	 * the next parameters as value.
	 */
	protected $_needValue = false;
	/**
	 * @var string This variable contains a value taken from the command
	 * line. It's used only when this is an option that requires values.
	 */
	protected $_value = false;
	//
	// Public methods.
	/**
	 * This method allows to know if this option has been found in the
	 * command line.
	 *
	 * @return boolean Returns true this option was found
	 */
	public function activated() {
		return $this->_activated;
	}
	/**
	 * This method allows to add a parameter that may activate this option.
	 *
	 * @param string $trigger This is the parameter to add as trigger. It
	 * should not have dashes at the beginning of it.
	 */
	public function addTrigger($trigger) {
		//
		// Checking if it's an extended parameter or not and adding the
		// appropriate number of dashes.
		if(strlen($trigger) > 1) {
			$trigger = "--{$trigger}";
		} else {
			$trigger = "-{$trigger}";
		}
		//
		// If this trigger is not yet added, it must be.
		if(!in_array($trigger, $this->_triggers)) {
			$this->_triggers[] = $trigger;
		}
	}
	/**
	 * This method checks this option against the command line.
	 *
	 * @param string[] List of parameters to use in addition to $argv.
	 */
	public function check($params = null) {
		//
		// Loading global pointers.
		global $argv;
		//
		// Setting activation flag to its default value.
		$this->_activated = false;
		//
		// Checking if there are extra parameters to use.
		$params = array_merge($argv, $params !== null ? $params : array());
		//
		// At first it's considered that the option doesn't need more
		// parameters.
		$needmore = false;
		//
		// Checking each parameter.
		foreach($params as $arg) {
			//
			// Checking if this parameter should be consumed as
			// trigger or as a value.
			if($needmore) {
				//
				// At this point, this parameter must be consumed
				// as value.
				$this->_value = $arg;
				//
				// This option is set as activated.
				$this->_activated = true;
				//
				// Avoiding to consume next parameter as value.
				$needmore = false;
			} elseif(in_array($arg, $this->_triggers)) {
				//
				// At this point, this parameter must be consumed
				// as trigger.
				//
				// Checking if this option requires values or if
				// it's a simple one.
				if($this->needValue()) {
					//
					// If it was set as found, now it's set
					// as new.
					$this->_activated = false;
					//
					// Previous values are erased.
					$this->_value = false;
					//
					// Setting the next round to consume as
					// value.
					$needmore = true;
				} else {
					//
					// Setting a basic value.
					$this->_value = true;
					//
					// Setting this option as found.
					$this->_activated = true;
				}
			}
		}
		//
		// At this point, is this option is still requiring a value, it
		// means there's something lost.
		if($needmore) {
			trigger_error(__CLASS__."::".__FUNCTION__.": Option '{$this->name()}' needs a value (triggers: '".implode("', '", $this->_triggers)."').", E_USER_WARNING);
		}
	}
	/**
	 * This method returns the name given to this option.
	 *
	 * @return string Returns this option's name.
	 */
	public function name() {
		return $this->_name;
	}
	/**
	 * This method allows to know if this option needs to consume the next
	 * parameters as value.
	 *
	 * @return boolean Returns true when this option requires the next
	 * value.
	 */
	public function needValue() {
		return $this->_needValue;
	}
	/**
	 * This method sets the name of this option.
	 *
	 * @param string $name Name to be set.
	 * @return string Returns this option's name.
	 */
	public function setName($name) {
		$this->_name = $name;
		return $this->name();
	}
	/**
	 * This method set this option to consume or not values from a command
	 * line.
	 *
	 * @param boolean $needs This flag indicates the status to be set. When
	 * absent, it's assumed true.
	 * @return boolean Returns true when this option requires the next
	 * value.
	 */
	public function setNeedValue($needs = true) {
		$this->_needValue = $needs;
		return $this->needValue();
	}
	/**
	 * This method allows to retrieve the value consumed by this option.
	 *
	 * @return string Returns this option's value.
	 */
	public function value() {
		//
		// Only when this option is activate and it requires values, this
		// method makes sense. Otherwise, returns false.
		if($this->activated() && $this->needValue()) {
			return $this->_value;
		} else {
			return false;
		}
	}
};

/**
 * @class WCOptions This singleton class holds the logic to manage a list of
 * options that may appear in a command line.
 */
class WCOptions {
	/**
	 * @var WCOptions This is the sole instance for this singleton class.
	 */
	protected static $_Instance;
	/**
	 * @var WCOption[string] List of option held by this singleton.
	 */
	protected $_options = array();
	/**
	 * @var boolean This flag indicates if the command line needs to be
	 * rechecked.
	 */
	protected $_recheck = true;
	/**
	 * Class constructor.
	 */
	protected function __construct() {
	}
	/**
	 * Prevent users from clone the singleton's instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods.
	/**
	 * This method allows to know if an option has been activated.
	 *
	 * @param string $name Name of the option to be checked.
	 * @return boolean Returns true when the option exists an it was
	 * activated.
	 */
	public function activated($name) {
		return isset($this->_options[$name]) && $this->_options[$name]->activated();
	}
	/**
	 * This method creates and adds a new simple option to this singleton.
	 *
	 * @param string $name Name to assign and identify the new option.
	 * @return boolean Returns true if the options was successfully added.
	 */
	public function addOption($name) {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking if this is not already added.
		if(!isset($this->_options[$name])) {
			//
			// Creating and adding a new option to the list.
			$this->_options[$name] = new WCOption();
			//
			// Setting option's name.
			$this->_options[$name]->setName($name);
			//
			// Adding a new option requires to re-check the command
			// line.
			$this->_recheck = true;
			//
			// At this point, a new option was successfully created
			// and added.
			$out = true;
		}
		//
		// Returning the addition result.
		return $out;
	}
	/**
	 * This method creates and adds a new option to this singleton. This new
	 * option will require a value.
	 *
	 * @param string $name Name to assign and identify the new option.
	 * @return boolean Returns true if the options was successfully added.
	 */
	public function addValueOption($name) {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Adding the option as if it were a simple one.
		if($this->addOption($name)) {
			//
			// Configuring option to require values.
			$this->_options[$name]->setNeedValue(true);
			//
			// At this point, it was successfully added.
			$out = true;
		}
		//
		// Returning the addition result.
		return $out;
	}
	/**
	 * This method allows to assign a trigger to certain option.
	 *
	 * @param string $name Name of the option to alter. If the option is not
	 * yet added, nothing is done.
	 * @param string $trigger This is the parameter to add as trigger. It
	 * should not have dashes at the beginning of it.
	 */
	public function addOptionTrigger($name, $trigger) {
		//
		// Checking if the option is present in the internal list.
		if(isset($this->_options[$name])) {
			//
			// Adding a trigger to the option.
			$this->_options[$name]->addTrigger($trigger);
			//
			// Changing an option requires to re-check the command
			// line.
			$this->_recheck = true;
		}
	}
	/**
	 * This method checks options against the command line.
	 *
	 * @param string[] List of parameters to use in addition to $argv.
	 */
	public function check($params = null) {
		$this->recheck($params);
	}
	/**
	 * This method removes all options from this singleton.
	 */
	public function clear() {
		//
		// Removing all options one by one.
		foreach($this->_options as $name => $opt) {
			unset($this->_options[$name]);
		}
		//
		// Restarting list of options.
		$this->_options = array();
	}
	/**
	 * This method allows to know if an option requires values.
	 *
	 * @param string $name Name of the option to be checked.
	 * @return boolean Returns true when the option exists an it requires
	 * values.
	 */
	public function needValue($name) {
		return isset($this->_options[$name]) && $this->_options[$name]->needValue();
	}
	/**
	 * This method allows to access directly to an option object.
	 *
	 * @param string $name Name of the option to look for.
	 * @return WCOption Returns option or false if it's not present.
	 */
	public function option($name) {
		//
		// Checking if the option is present.
		if(isset($this->_options[$name])) {
			//
			// Returning the option.
			return $this->_options[$name];
		} else {
			//
			// At this point, the option is not present, therefore
			// false is returned.
			return false;
		}
	}
	/**
	 * This method allows to retrieve the value consumed by certain option.
	 *
	 * @param string $name Name of the option to look for.
	 * @return string Returns this option's value. When the option is not
	 * present, it returns false.
	 */
	public function value($name) {
		//
		// Checking if the option is present.
		if(isset($this->_options[$name])) {
			//
			// Returning option's value.
			return $this->_options[$name]->value();
		} else {
			//
			// At this point, the option is not present, false is
			// returned.
			return false;
		}
	}
	//
	// Protected methods.
	/**
	 * This method walks over every option and parameter from a command line
	 * trying to know which one is activated.
	 *
	 * @param string[] $params This is a list of extra parameters to check.
	 * When it's not given, only $argv is checked.
	 */
	protected function recheck($params = null) {
		//
		// Only when it's indicated that parameters have to be rechecked
		// or when there's a set of extra parameters to check is when
		// this method takes effect.
		$check = $this->_recheck || $params !== null;
		//
		// Checking whether options have to be checked or not.
		if($check) {
			//
			// Walking over each option.
			foreach($this->_options as $name => $opt) {
				//
				// Checking parameters against this option.
				$this->_options[$name]->check($params);
			}
		}
	}
	//
	// Public class methods.
	/**
	 * This class-method creates and returns the sole instance for this
	 * singleton class.
	 *
	 * @return WCOptions Singleton instance.
	 */
	public static function Instance() {
		//
		// Checking if the instance is already created.
		if(!isset(self::$_Instance)) {
			// Creating the first instance.
			$c = __CLASS__;
			self::$_Instance = new $c();
		}
		//
		// Returning the singleton's pointer.
		return self::$_Instance;
	}
}
?>